/*
 * Image.cpp
 *
 *  Created on: 12/09/2012
 *      Author: leandro
 */

#include "Image.h"
#include <math.h>

#define min(a, b)  (((a) < (b)) ? (a) : (b)) 
#define max(a, b)  (((a) > (b)) ? (a) : (b))

SDL_Surface* cargar_imagen( std::string filename )
{
    SDL_Surface* imagen = NULL;
    SDL_Surface* nueva_imagen = NULL;

    imagen = SDL_LoadBMP( filename.c_str() );
    if ( imagen == NULL ) imagen = IMG_Load( filename.c_str() );

    if( imagen != NULL )
    {
        nueva_imagen = SDL_DisplayFormat( imagen );

        SDL_FreeSurface( imagen );

        if( nueva_imagen != NULL )
        {
            colorKey(nueva_imagen);
        }
    }

    return nueva_imagen;
}

Uint32 get_pixel(SDL_Surface *surface, int x, int y)
{
    int bpp = surface->format->BytesPerPixel;
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {

    case 1:

        return *p;
        break;

    case 2:

        return *(Uint16 *)p;
        break;

    case 3:

        if ( SDL_BYTEORDER == SDL_BIG_ENDIAN )
        {
            return p[0] << 16 | p[1] << 8 | p[2];

        } else {

            return p[0] | p[1] << 8 | p[2] << 16;

        }

        break;

    case 4:

        return *(Uint32 *)p;
        break;

    default:
        return 0;

    }
}

void set_pixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
    int bpp = surface->format->BytesPerPixel;
    Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;

    switch(bpp) {

    case 1:

        *p = pixel;
        break;

    case 2:

        *(Uint16 *)p = pixel;
        break;

    case 3:

        if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
        {
            p[0] = (pixel >> 16) & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = pixel & 0xff;

        } else {

            p[0] = pixel & 0xff;
            p[1] = (pixel >> 8) & 0xff;
            p[2] = (pixel >> 16) & 0xff;

        }

        break;

    case 4:

        *(Uint32 *)p = pixel;
        break;

    }
}

SDL_Surface* espejarY(SDL_Surface* imagen) {

	SDL_Surface* nuevaImg = SDL_CreateRGBSurface( SDL_HWSURFACE, imagen->w, imagen->h, imagen->format->BitsPerPixel, imagen->format->Rmask, imagen->format->Gmask, imagen->format->Bmask, imagen->format->Amask);

	for(int i = 0; i < imagen->h; i++) {
		for(int x = 0; x < imagen->w; x++) {
			set_pixel(nuevaImg, x, i, get_pixel(imagen, x, imagen->h - i - 1));
		}
	}
	return nuevaImg;
}

SDL_Surface* espejarX(SDL_Surface* imagen) {

	SDL_Surface* nuevaImg = SDL_CreateRGBSurface( SDL_HWSURFACE, imagen->w, imagen->h, imagen->format->BitsPerPixel, imagen->format->Rmask, imagen->format->Gmask, imagen->format->Bmask, imagen->format->Amask);

	for(int i = 0; i < imagen->h; i++) {
		for(int x = 0; x < imagen->w; x++) {
			set_pixel(nuevaImg, x, i, get_pixel(imagen, imagen->w - x - 1, i));
		}
	}
	return nuevaImg;
}

void colorKey(SDL_Surface *surface, int color_r, int color_b, int color_g) {
	SDL_SetColorKey(surface, SDL_SRCCOLORKEY, SDL_MapRGB( surface->format, color_r, color_b, color_g) );
}

SDL_Surface* ajustar_imagen(SDL_Surface *Surface, int Width, int Height, int color_r, int color_b, int color_g)
{

    if(!Surface || !Width || !Height)
        return 0;

    SDL_Surface* ret = SDL_CreateRGBSurface( Surface->flags, Width, Height, Surface->format->BitsPerPixel,
        Surface->format->Rmask, Surface->format->Gmask, Surface->format->Bmask, Surface->format->Amask );

    double stretch_factor_x = ( static_cast<double>( Width )  / static_cast<double>( Surface->w ) ),
    	   stretch_factor_y = ( static_cast<double>( Height ) / static_cast<double>( Surface->h ) );

    for ( Sint32 y = 0; y < Surface->h; y++ )

        for( Sint32 x = 0; x < Surface->w; x++ )

            for( Sint32 o_y = 0; o_y < stretch_factor_y; ++o_y )

                for( Sint32 o_x = 0; o_x < stretch_factor_x; ++o_x )

                    set_pixel(ret, static_cast<Sint32>( stretch_factor_x * x ) + o_x,
                    static_cast<Sint32>( stretch_factor_y * y ) + o_y, get_pixel( Surface, x, y ) );

    colorKey(ret, color_r, color_b, color_g);

    return ret;
}

int insertar_imagen( int x, int y, SDL_Surface* fuente, SDL_Surface* destino, SDL_Rect* clip)
{
    SDL_Rect posicion;

    posicion.x = x;
    posicion.y = y;

    return (SDL_BlitSurface( fuente, clip, destino, &posicion ));

}

Uint32 get_pixel32( SDL_Surface *surface, int x, int y )
{
    //Convert the pixels to 32 bit
    Uint32 *pixels = (Uint32 *)surface->pixels;

    //Get the requested pixel
    return pixels[ ( y * surface->w ) + x ];
}

void put_pixel32( SDL_Surface *surface, int x, int y, Uint32 pixel )
{
    //Convert the pixels to 32 bit
    Uint32 *pixels = (Uint32 *)surface->pixels;

    //Set the pixel
    pixels[ ( y * surface->w ) + x ] = pixel;
}

SDL_Surface *rotar_imagen( SDL_Surface *surface, int angulo )
{
    SDL_Surface *destino = NULL;
    
	float radianes = 2 * 3.1416 * (360 - angulo) / 360;
	float coseno = (float)cos(radianes); 
	float seno = (float)sin(radianes); 

	float point1x = -surface->h*seno;
	float point1y = surface->h*coseno;
	float point2x = surface->w*coseno - surface->h*seno;
	float point2y = surface->h*coseno + surface->w*seno;
	float point3x = surface->w*coseno;
	float point3y = surface->w*seno;

	float minx = min (0, min(point1x, min(point2x, point3x))); 
	float miny = min (0, min(point1y,min(point2y, point3y))); 
	float maxx = max (point1x, max(point2x, point3x)); 
	float maxy = max (point1y, max(point2y, point3y)); 
	
	int destino_width = (int)ceil(fabs(maxx) - minx); 
	int destino_height = (int)ceil(fabs(maxy) - miny);

	destino = SDL_CreateRGBSurface( SDL_SWSURFACE, destino_width, destino_height, surface->format->BitsPerPixel, surface->format->Rmask, surface->format->Gmask, surface->format->Bmask, surface->format->Amask );
    SDL_FillRect(destino, NULL, SDL_MapRGB(destino->format, 0, 255, 255));
    
    if( SDL_MUSTLOCK( surface ) )
    {
        SDL_LockSurface( surface );
    }

    for( int x = 0; x < destino->w; x++ )
    {
        for( int y = 0; y < destino->h; y++ )
        {
			int surfacex = (int)( (x + minx)*coseno + (y + miny)*seno ); 
			int surfacey = (int)( (y + miny)*coseno - (x + minx)*seno ); 
			if(surfacex >= 0 && surfacex < surface->w && surfacey >= 0 && surfacey < surface->h) 
			{
				Uint32 pixel = get_pixel32( surface, surfacex, surfacey );
				put_pixel32( destino, x, y, pixel );
			}
			
        }
    }

    if( SDL_MUSTLOCK( surface ) )
    {
        SDL_UnlockSurface( surface );
    }

    colorKey(destino);
    return destino;
}

